import "./style.css"

import paper from "paper"

paper.setup(document.querySelector<HTMLCanvasElement>("#canvas")!)

const ballPositions = [
  [255, 129],
  [610, 73],
  [486, 363],
  [117, 459],
  [484, 726],
  [843, 306],
  [789, 615],
  [1049, 82],
  [1292, 428],
  [1117, 733],
  [1352, 86],
  [92, 798],
]

const handle_len_rate = 2.4
const circlePaths: paper.Path.Circle[] = []
for (var i = 0, l = ballPositions.length; i < l; i++) {
  const circlePath = new paper.Path.Circle({
    center: ballPositions[i],
    radius: 50,
  })

  circlePath.fillColor = "black"

  circlePaths.push(circlePath)
}

const largeCircle = new paper.Path.Circle({
  center: [676, 433],
  radius: 100,
})

largeCircle.fillColor = "black"
circlePaths.push(largeCircle)

const tool1 = new paper.Tool()
tool1.onMouseDrag = (event) => {
  largeCircle.position = event.point
  generateConnections(circlePaths)
}

var connections = new paper.Group()
function generateConnections(paths) {
  // Remove the last connection paths:
  connections.children = []

  for (var i = 0, l = paths.length; i < l; i++) {
    for (var j = i - 1; j >= 0; j--) {
      var path = metaball(paths[i], paths[j], 0.5, handle_len_rate, 300)
      if (path) {
        connections.appendTop(path)
        path.removeOnMove()
      }
    }
  }
}

generateConnections(circlePaths)

// ---------------------------------------------
function metaball(
  ball1: paper.Path,
  ball2: paper.Path,
  v: number,
  handle_len_rate: number,
  maxDistance: number
) {
  var center1 = ball1.position
  var center2 = ball2.position
  var radius1 = ball1.bounds.width / 2
  var radius2 = ball2.bounds.width / 2
  var pi2 = Math.PI / 2
  var d = center1.getDistance(center2)
  var u1, u2

  if (radius1 == 0 || radius2 == 0) return

  if (d > maxDistance || d <= Math.abs(radius1 - radius2)) {
    return
  } else if (d < radius1 + radius2) {
    // case circles are overlapping
    u1 = Math.acos(
      (radius1 * radius1 + d * d - radius2 * radius2) / (2 * radius1 * d)
    )
    u2 = Math.acos(
      (radius2 * radius2 + d * d - radius1 * radius1) / (2 * radius2 * d)
    )
  } else {
    u1 = 0
    u2 = 0
  }

  var angle1 = center2.subtract(center1).getAngleInRadians(center1)
  var angle2 = Math.acos((radius1 - radius2) / d)
  var angle1a = angle1 + u1 + (angle2 - u1) * v
  var angle1b = angle1 - u1 - (angle2 - u1) * v
  var angle2a = angle1 + Math.PI - u2 - (Math.PI - u2 - angle2) * v
  var angle2b = angle1 - Math.PI + u2 + (Math.PI - u2 - angle2) * v
  var p1a = center1.add(getVector(angle1a, radius1))
  var p1b = center1.add(getVector(angle1b, radius1))
  var p2a = center2.add(getVector(angle2a, radius2))
  var p2b = center2.add(getVector(angle2b, radius2))

  // define handle length by the distance between
  // both ends of the curve to draw
  var totalRadius = radius1 + radius2
  var d2 = Math.min(v * handle_len_rate, p1a.subtract(p2a).length / totalRadius)

  // case circles are overlapping:
  d2 *= Math.min(1, (d * 2) / (radius1 + radius2))

  radius1 *= d2
  radius2 *= d2

  var path = new paper.Path({
    segments: [p1a, p2a, p2b, p1b],
    style: ball1.style,
    closed: true,
  })
  var segments = path.segments
  segments[0].handleOut = getVector(angle1a - pi2, radius1)
  segments[1].handleIn = getVector(angle2a + pi2, radius2)
  segments[2].handleOut = getVector(angle2b - pi2, radius2)
  segments[3].handleIn = getVector(angle1b + pi2, radius1)
  return path
}

// ------------------------------------------------
function getVector(radians: number, length: number) {
  return new paper.Point({
    // Convert radians to degrees:
    angle: (radians * 180) / Math.PI,
    length: length,
  })
}
